package gl.java.javascript;

import gl.java.game.Timer;
import gl.java.umsp.UmspHeader;
import gl.java.umsp.bean.Room;
import lombok.extern.slf4j.Slf4j;

import javax.script.*;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

/**
 * 使用java内置的脚本引擎
 * 核心接口 @see #javax.script.Compilable and javax.script.Invocable <br/>
 * <br>javax.script.ScriptManger</>
 */
@Slf4j
public class ScriptManger {
    private ScriptEngine engine;

    public void setup(String scriptDir) {
        engine = new ScriptEngineManager().getEngineByExtension("js");
        injectGlobal(new JavaScriptWindow());
        loadScript(new File(scriptDir));
    }

    public void injectGlobal(JavaScriptWindow window) {
        window.attach(engine);
    }

    public void loadScript(File file) {
        if (file.isDirectory()) {
            File[] child = file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory()||pathname.getName().endsWith(".js");
                }
            });
            for (java.io.File f : child) {
                loadScript(f);
            }
        } else {
            try {
                engine.eval(new FileReader(file));
                log.info("load script:"+file.getName());
            } catch (ScriptException e) {
                log.error(file.getName() + ":" + e.getMessage(), e);
            } catch (FileNotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }

    }

    public javax.script.ScriptEngine getEngine() {
        return engine;
    }

    private final static int ERR_LOAD_SCRIPT_BY_PATH = 1;
    private final static int ERR_NOTHING = 0;
    private final static int ERR_LOAD_SCRIPT_BY_URL = 2;
    private volatile static int lastError = ERR_NOTHING;
    public final static Object NULL = new Object() {
        public String toString() {
            return "";
        }
    };

    /**
     * 加载指定路径文件中的的脚本
     *
     * @param path
     * @throws FileNotFoundException
     * @throws ScriptException
     */
    public Object loadScriptFileByPath(String path) throws FileNotFoundException, ScriptException {
        try {
            return engine.eval(new FileReader(path));
        } catch (Exception e) {
            log.warn(e.toString());
            lastError = ERR_LOAD_SCRIPT_BY_PATH;
            return null;
        }
    }

    public CompiledScript compileScriptFileByPath(String path) throws FileNotFoundException, ScriptException {
        try {
            return ((Compilable) engine).compile(new FileReader(path));
        } catch (Exception e) {
            log.warn(e.toString());
            lastError = ERR_LOAD_SCRIPT_BY_PATH;
        }
        return null;
    }

    /**
     * 加载指定url的脚本,支持http协议
     *
     * @param url
     * @throws ScriptException
     */
    public void loadScriptFileByUrl(String url) throws ScriptException {
        try {
            engine.eval("load('" + url + "')");
        } catch (Exception e) {
            log.warn(e.toString());
            lastError = ERR_LOAD_SCRIPT_BY_URL;
        }

    }

    /**
     * java 调用 js的函数
     *
     * @param name js的函数名
     * @param args 参数
     * @param <T>  返回值类型
     * @return
     */
    public <T> Object invokeFunction(String name, Object... args) {
        if (lastError != ERR_NOTHING) {
            return NULL;
        }
        try {
            return ((Invocable) engine).invokeFunction(name, args);
        } catch (Exception e) {
            log.warn(e.toString());
            return NULL;
        }
    }

    /**
     * 向javascript 注入java对象
     *
     * @param objRefName 被注入对象在js中引用名
     * @param obj        被注入的对象
     */
    public void injectJavaObject(String objRefName, Object obj) {
        engine.put(objRefName, obj);
    }


    public static String onUserSendMsg(String user, String room, String msg) {
        return user + room + msg;
    }

    public static void testJavaScriptCallJava(ScriptEngine engine) throws ScriptException {
        File f = new File("test.txt");
        //将File对象f直接注入到js脚本中并可以作为全局变量使用
        engine.put("file", f);

//        evaluate a script string.The script accesses "file"
//        variable and calls method on it
        engine.eval("print(file.getAbsolutePath())");
    }

    private static void testHashMapAndNewJavaObject() {
        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine nashorn = factory.getEngineByName("nashorn");
        try {
            nashorn.eval(""
                    + "var ScriptManger= Java.type('gl.java.javascript.ScriptManger');"
                    + "var HashMap = Java.type(\"java.util.HashMap\");"
                    + "var map = new HashMap();"
                    + "map.put(0, \"value1\");"
                    + "var test = new ScriptManger();"
                    + "test.test(map);"
                    + "");
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    public void test(Map<String, String> obj) {
        System.out.println(obj);
    }

    private void testTimer() throws ScriptException {
        engine.eval("var loop1 = setInterval(function () {" +
                "    console.info('[INFO] run ->loop.id:{}, this:{}' ,loop1,this);" +
                "}.bind(this), 3000);console.info('[INFO] setInterval  loop.id:{}' ,loop1);");

    }

    public static void main(String[] args) {
        try {

            String dir = "F:\\netty\\Umsp\\Umsp\\server\\script\\";
            String name = "lambda.js";

            ScriptManger scriptManger = new ScriptManger();
            scriptManger.setup(dir);

//
//        UmspHeader msg = new UmspHeader();
//        Room room = new Room(0);

//        long t1 = System.nanoTime();
//        for (int i = 0; i < 1000 * 10000; i++) {
//            onUserSendMsg("1", "2", "3");
//        }
//        long t2 = System.nanoTime();
//        log.info("100 0000 time java`invoke: take {} ms", (t2 - t1) / 1000 / 1000);


//        String name = "match.js";

//        CompiledScript engine1 = ScriptManger.compileScriptFileByPath(dir + name);
//        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
//        bindings.put("a1", "");
//        bindings.put("a2", "");
//        bindings.put("a3", "");
//
            long t3 = System.nanoTime();

//        for (int i = 0; i < 1000 * 10000; i++) {
//            engine1.eval(bindings);
//        }

//        String methodName = (String) ScriptManger.loadScriptFileByPath(dir + name);
//        for (int i = 0; i < 1000 * 10000; i++) {
//            ScriptManger.invokeFunction(methodName, "", "", "");
//        }
//        long t4 = System.nanoTime();
//        log.info("100 0000 time js`invoke: take {} ms", (t4 - t3) / 1000 / 1000);
//        log.info("return {}", ScriptManger.invokeFunction(methodName, "1", "2", "3"));

            Map<String, Object> map = new HashMap();


//
//        javax.script.ScriptEngine e = scriptManger.engine;
//        map.put("onUserMatch", scriptManger.loadScriptFileByPath(dir + "onUserMatch.js"));
//        map.put("onUserSendMsg", scriptManger.loadScriptFileByPath(dir + "lambda.js"));
//
//        for (String s : map.keySet()) {
//            log.info("{} return {}", s, scriptManger.invokeFunction(s, "1", "2", "3"));
//        }
//        for (String s : map.keySet()) {
//            log.info("{} return {}", s, scriptManger.invokeFunction(s, "1", "2", "3"));
//        }


            log.info("over");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
